Part VI
MFC and the Web

In This Part

  MFC and DHTML 817
  CSocket Programming 851
  WinInet Programming 885
  ISAPI Extensions 935
  MAPI and MFC 969
  TAPI and MFC 987

Chapter 23
MFC and DHTML

by Kenn Scribner

In This Chapter

  DHTML, MSIE, and the Internet 818
  Using the Internet Explorer Web ActiveX Control 819
  Using DHTML 829

DHTML, MSIE, and the Internet

Until the introduction of dynamic HTML, Web pages were static ones presented to the browser. HTML (Hypertext Markup Language) was introduced to describe the visual formatting of information sent to Web browsers. After the page was published on the Web, the HTML-formatted information was simply to be viewed. If anything was to be changed, a request would be issued to and response received from the Web server, resulting in new HTML information being shipped to the client system.

When Microsoft designed Internet Explorer (IE) 4, though, they changed the rules of the game. Along with the usual Web browser functionality, Microsoft designed an HTML object model it called Dynamic HTML (DHTML). The whole idea behind DHTML was to allow scripted elements embedded within the HTML document to access and modify the contents of the Web page without interaction with the Web server. This makes for more snappy Web page performance, especially when the user’s Internet connection is over a modem.

It wasn’t long after DHTML’s introduction that programmers realized DHTML was easily accessible from Visual Basic. The core of the Internet Explorer Web browser is an ActiveX control, and because ActiveX controls were originally designed to replace the Visual Basic Extension (VBX) corols, the IE ActiveX control (I’ll call it MSIE for short) was and is a natural fit within Visual Basic.

I say “just another” ActiveX control, but this particular control packs a wallop. The Internet Explorer Web browser you see when you run IE 4 is a thin application built around this ActiveX control. MSIE handles the entire Web browsing task for the application, which includes visual formatting and rendering, HTML parsing, and even such arcane tasks as the user’s “favorites” storage and retrieval. It manages its own window, handles server downloads asynchronously (and is multithreaded), and deals with several protocols such as the popular File Transfer Protocol(FTP) and Hypertext Transport Protocol (HTTP).

In addition to the browsing aspects, it provides you with DHTML. What this really means is that you are now able to access the browser’s HTML document, parse through the HTML, and do with the results as your application requires. The truly exciting part is that you aren’t limited to read-only HTML access—you can also modify the HTML contents on-the-fly.


Tip:  

The sample programs in this chapter don’t do DHTML justice from an artistic standpoint (that subject is a book in itself). They will show you how to access DHTML from Visual C++. I highly recommend that you visit the Microsoft Web site to see some of the amazing things you can do with DHTML. Many of Microsoft’s pages use DHTML to enhance the page’s impact or present information in a new and different way.


In this chapter you’ll see some of the exciting things you can do with DHTML using MFC. But before I get into the details of DHTML, I need to explain how you use the MSIE ActiveX control, both in a document/view scenario and from a dialog box. After you have MSIE working in your application, you can invoke its DHTML COM interfaces.

Using the Internet Explorer Web ActiveX Control

When using MSIE with Visual C++ 6, you have two primary alternatives. First, if you’re interested in using the standard MFC document/view architecture, you can use the new CHtmlView class in place of CView, or some other view class. And second, you can insert MSIE into a dialog box just as you would any other ActiveX control. Both methods are very easy to implement at least as far as activating the browser control is concerned. I’ll begin with an overview of the basic capabilities of the control and then discuss its use in your applications.

Internet Explorer ActiveX Control Basics

The total functionality MSIE exposes comes from many files, but the primary file is named shdocvw.dll. This file is MSIE for all intents and purposes. The other files have more to do with Internet communication than the actual browsing process (see Chapter 25, “WinInet Programming”). In this section, I’ll address the browsing aspect of MSIE. I’ll defer the DHTML aspect until later in the chapter.

MSIE is an ActiveX control, and as such it also exposes several COM interfaces. The Web browser COM interface itself is called IWebBrowser2. Probably the easiest way to grasp what this interface provides you is to examine Listing 23.1, which is the code Developer Studio generates for you when you insert MSIE into your application. The length of Listing 23. 1 alone gives you some idea as to the power this control wields!


Note:  

The function names and parameters you see in Listing 23.1 are valid only when MSIE is inserted into your application by Developer Studio. As with all ActiveX controls you insert, Developer Studio queries the ActiveX control for the methods and properties it exposes and creates a C++ wrapper for you. This is what you see in Listing 23.1. The effect of this is that the method signatures differ if you call IWebBrowser2 directly. There is a direct correlation, though, so reading about IWebBrowser2 in the online help should help you understand the use of functions in Listing 23.1 that aren’t readily apparent to you at first glance.




Listing 23.1 Webbroswer2.h, the Generated IWebBrowser2 Declaration File


#if !defined(AFX_WEBBROWSER2_H__A1582E0F_A1BC_11D2_8329
Ä_B28A8D2EBF19__INCLUDED_)
#define AFX_WEBBROWSER2_H__A1582E0F_A1BC_11D2_8329
Ä_B28A8D2EBF19__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Machine generated IDispatch wrapper class(es) created by Microsoft
// Visual C++

// NOTE: Do not modify the contents of this file. If this class
is
// regenerated by Microsoft Visual C++, your modifications
// will be overwritten.

/////////////////////////////////////////////////////////////////////
// CWebBrowser2 wrapper class

class CWebBrowser2 : public CWnd
{
protected:
   DECLARE_DYNCREATE(CWebBrowser2)
public:
   CLSID const& GetClsid()
   {
      static CLSID const clsid
         = { 0x8856f961, 0x340a, 0x11d0,
           Ä{ 0xa9, 0x6b, 0x0, 0xc0, 0x4f, 0xd7, 0x5, 0xa2 } };
      return clsid;
   }
   virtual BOOL Create(LPCTSTR lpszClassName,
      LPCTSTR lpszWindowName, DWORD dwStyle,
      const RECT& rect,
      CWnd* pParentWnd, UINT nID,
      CCreateContext* pContext = NULL)
   { return CreateControl(GetClsid(), lpszWindowName, dwStyle,
   Ärect, pParentWnd, nID); }

    BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,
      const RECT& rect, CWnd* pParentWnd, UINT nID,
      CFile* pPersist = NULL, BOOL bStorage = FALSE,
      BSTR bstrLicKey = NULL)
   { return CreateControl(GetClsid(), lpszWindowName,
   ÄdwStyle, rect, pParentWnd, nID,
      pPersist, bStorage, bstrLicKey); }

// Attributes
public:

// Operations
public:
   void GoBack();
   void GoForward();
   void GoHome();
   void GoSearch();
   void Navigate(LPCTSTR URL, VARIANT* Flags, VARIANT*
   ÄTargetFrameName, VARIANT* PostData, VARIANT* Headers);
   void Refresh();
   void Refresh2(VARIANT* Level);
   void Stop();
   LPDISPATCH GetApplication();
   LPDISPATCH GetParent();
   LPDISPATCH GetContainer();
   LPDISPATCH GetDocument();
   BOOL GetTopLevelContainer();
   CString GetType();
   long GetLeft();
   void SetLeft(long nNewValue);
   long GetTop();
   void SetTop(long nNewValue);
   long GetWidth();
   void SetWidth(long nNewValue);
   long GetHeight();
   void SetHeight(long nNewValue);
   CString GetLocationName();
   CString GetLocationURL();
   BOOL GetBusy();
   void Quit();
   void ClientToWindow(long* pcx, long* pcy);
   void PutProperty(LPCTSTR Property_, const VARIANT& vtValue);
   VARIANT GetProperty_(LPCTSTR Property_);
   CString GetName();
   long GetHwnd();
   CString GetFullName();
   CString GetPath();
   BOOL GetVisible();
   void SetVisible(BOOL bNewValue);
   BOOL GetStatusBar();
   void SetStatusBar(BOOL bNewValue);
   CString GetStatusText();
   void SetStatusText(LPCTSTR lpszNewValue);
   long GetToolBar();
   void SetToolBar(long nNewValue);
   BOOL GetMenuBar();
   void SetMenuBar(BOOL bNewValue);
   BOOL GetFullScreen();
   void SetFullScreen(BOOL bNewValue);
   void Navigate2(VARIANT* URL, VARIANT* Flags, VARIANT*
   ÄTargetFrameName, VARIANT* PostData, VARIANT* Headers);
   long QueryStatusWB(long cmdID);
   void ExecWB(long cmdID, long cmdexecopt, VARIANT* pvaIn,
   ÄVARIANT* pvaOut);
   void ShowBrowserBar(VARIANT* pvaClsid, VARIANT* pvarShow,
   ÄVARIANT* pvarSize);
   long GetReadyState();
   BOOL GetOffline();
   void SetOffline(BOOL bNewValue);
   BOOL GetSilent();
   void SetSilent(BOOL bNewValue);
   BOOL GetRegisterAsBrowser();
   void SetRegisterAsBrowser(BOOL bNewValue);
   BOOL GetRegisterAsDropTarget();
   void SetRegisterAsDropTarget(BOOL bNewValue);
   BOOL GetTheaterMode();
   void SetTheaterMode(BOOL bNewValue);
   BOOL GetAddressBar();
   void SetAddressBar(BOOL bNewValue);
   BOOL GetResizable();
   void SetResizable(BOOL bNewValue);
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations
// immediately before the previous line.

#endif // !defined(AFX_WEBBROWSER2_H__A1582E0F_A1BC_11D2_8329
       im// _B28A8D2EBF19__INCLUDED_)

As you see from Listing 23.1, you have quite a bit of control over the browser aspects of MSIE. Look especially at the operations section. Here you see you can tailor aspects such as whether the control displays one or all of several different menu bars (address, menu, and so on), whether the browser window is full screen or not, and when and to where you navigate (forward, back, general navigation, and cancellation). When you use the DHTML aspects of the MSIE control you’ll see later in the chapter, you’ll use the very important GetDocument() method, which gives you access to the HTML document the browser is currently displaying.

What Listing 23.1 doesn’t show you is the large number of events the control exposes. Events, as you may remember, are notifications from the control, such as when a navigation command has completed, or when a download has begun. Table 23.1 shows you the events MSIE fires.

Table 23.1 MSIE Events

Event Meaning

BeforeNavigate2 Preparing to navigate.
NavigateComplete2 Navigation completed.
CommandStateChange Command-enabled state changed.
DocumentComplete READYSTATE_COMPLETE.
DownloadBegin Navigation is beginning.
DownloadComplete Navigation finished, halted, or failed.
NewWindow2 A new window was created.
OnMenuBar The MenuBar property changed.
OnToolBar The ToolBar property changed.
OnStatusBar The StatusBar property changed.
OnFullScreen The FullScreen property changed.
OnTheaterMode The TheaterMode property changed.
OnVisible Containing window should be shown/hidden.
OnQuit MSIE is ready to quit.
ProgressChange Download progress updated.
PropertyChange PutProperty method changed a property.
StatusTextChange Status bar text changed.
TitleChange Document title is available or changed.



You’ll use the methods shown in Listing 23.1 and the events indicated in Table 23.1 when you deal with CHtmlView and the ActiveX control directly (such as when using MSIE from within a dialog box). Because this control is so rich in functionality, it might be most informative if I describe it further by using it from within an application. I’ll turn now to CHtmlView.

CHtmlView

CHtmlView is a thin wrapper around MSIE. In fact, most of the CHtmlView-specific members delegate their functionality directly to the MSIE control, which the CFormView-based view creates when it itself is created. For example, here is the source code from the MFC source file Viewhtml.cpp for the CHtmlView::GetHtmlDocument() method:

LPDISPATCH CHtmlView::GetHtmlDocument() const
{
   ASSERT(m_pBrowserApp != NULL);

   LPDISPATCH result;
   m_pBrowserApp->get_Document(&result);
   return result;
}

You determine whether your application will use CHtmlView when you create your project using the MFC AppWizard. When you reach the last AppWizard step, step 6 (see Figure 23.1), choose CHtmlView as your view’s base class.


Figure 23.1  MFC AppWizard view selection dialog (step 6).

This chapter has two sample programs using CHtmlView. The first, WebApp, retrieves a few selected DHTML tags from the document and displays them in a tree control. The second, DocNavigator, pulls the bookmark anchors from an HTML document and allows you to navigate to the bookmarked position within the document (again using a tree control). You can see the WebApp application in action in Figure 23.2.


Figure 23.2  The WebApp sample program.

Although I’ll discuss the DHTML aspects of these programs later in the chapter, now is a good time to examine how you know when to access the DHTML objects. When the application navigates to a new URL (using CHtmlView::Navigate2()), the URL’s HTML document is loaded into the browser. When the browser completes this loading process, it fires the DocumentComplete event. For example, the WebApp sample program contains this code:

void CWebView::OnDocumentComplete(LPCSTSR lpszURL)
{
   CHtmlView::OnDocumentComplete(lpszURL);

   // Update the tree control
   CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
   pFrame->UpdateTree(GetHtmlDocument(),lpszURL);
}

When the HTML document has completely loaded, MSIE fires the DocumentComplete event, which is intercepted by the preceding code. In this case, the frame class notifies the tree control that the document has changed and allows it to query the HTML document for the information it requires to recreate itself (a tree view of the HTML document). Of course, at this time you can do anything you want to the HTML document—it is now stored within MSIE and available for your use.

CHtmlView and the Document/View Relationship

Notice that I said the HTML document is stored within MSIE. Put simply, CHtmlView doesn’t require the CDocument-derived class for document management even though MSIE clearly stores an (HTML) document. The control itself handles the HTML document management, leaving the CDocument-derived class to be used for other tasks, if you so choose.

At first glance, this in itself seems to be of little impact. After all, many MFC applications generated by the MFC AppWizard use the document/view architecture only to store all (or most) pertinent information within the view class. Many even avoid the document class (be honest). But this has (at least) one important ramification with respect to CHtmlView. The default document loading mechanism MFC employs has no effect upon MSIE. If you tried to load an HTML document into your CHtmlView-enabled application and expected the application to show your document by default, you would be disappointed. You would see nothing more than a blank view.

Knowing this, you must provide the document loading mechanism by overriding CHtmlView::OnFileOpen(). Listing 23.2 shows the code WebApp uses.

Listing 23.2 CWebView’s HTML Document File Load Method


void CWebView::OnFileOpen()
{
   // File filter
   static TCHAR BASED_CODE szHTMLFilter[] =
   Ä_T(“HTM Files (*.htm)|*.htm|HTML Files (*.htm)|*.htm|
   ÄAll Files (*.*)|*.*||”);

   // Bring up a file dialog to browse for HTML files.
   CFileDialog dlgHTML(TRUE,
               _T(“htm”),
               NULL,
               OFN_EXPLORER,
               szHTMLFilter,
               this);

   dlgHTML.m_ofn.lpstrTitle = _T(“Open HTML file”);

   if ( dlgHTML.DoModal() != IDOK ) {
      // User canceled...
      return;
   } // if

   // Pull file location
   CString strUrl(_T(“file://”));
   strUrl ++= dlgHTML.GetPathName();

   // Open it
   Navigate2(strUrl,NULL,NULL);
}

In this case, I allow the user to browse locally for the HTML document he or she wants to load, and then when the document is located, I create the appropriate URL, using the file protocol, and have MSIE load the docume using its navigate method. There is no requirement stipulating that the document must be a local HTML file—any URL would do. This is just how I implemented HTML file loading for the WebApp sample program. Feel free to load an HTML file as your requirements dictate, including even remote Internet documents.


Note:  

You do not necessarily need to format the URL to include the file protocol. I did so because it is the formally correct way to establish the URL to indicate a local HTML file. However, if you plan on loading remote documents through the Internet/intranet, you must format the URL to include either the HTTP or FTP protocol. In the remote case, your application will be communicating with a distant server and the transmission protocol must be specified.


CHtmlView and COM

One of the truly nice features about CHtmlView is the manner in which you interact with MSIE. The MSIE wrapper methods hide the COM nature of the MSIE control, enabling you to simply make method calls without regard to satisfying COM function call requirements. For many applications, this simplifies your code.

However, the underlying CHtmlView mechanism is MSIE, which is a COM object. Some of the CHtmlView method calls, such as GetHtmlDocument(), return to you a COM pointer (IDispatch*, in this case). All the rules of COM now apply in full force when using the IDispatch interface! Be sure to AddRef() the pointer when you first receive it and Release() it when you’re through with it.



CHtmlView provides a wonderful view class with DHTML functionality. However, it is also possible to use MSIE from within a dialog box. That is the topic for the next section.

Using the Internet Explorer ActiveX Control in a Dialog Box

Dialog boxes and ActiveX controls are a natural fit, and it isn’t much of a surprise to find that MSIE works well from within your application’s dialog box(es). The only difference between using CHtmlView and MSIE directly from a dialog box is that the wrapper functions change slightly. Instead of using CHtmlView’s wrappers, you use the wrapper functions Developer Studio creates for you when you insert the control. Although you saw the basic mechanics of loading an external ActiveX control into your dialog box in Chapter 15, “MFC ActiveX Control Containers,” I’ll recap the high points. These are the steps I took when I created this chapter’s WebDlg sample program:

1.  While Developer Studio is running with your project loaded, activate the Component Gallery dialog. You do this from the Developer Studio menu by clicking Project, Add to Project, Components and Controls.
2.  Select the Registered ActiveX Controls folder in the list control.
3.  Scroll the resulting list control horizontally until you see the Microsoft Web Browser ActiveX control. When the control is in view, either double-click it to insert it into your project or single-click the item and click the dialog’s Insert button. Click OK when prompted to confirm the insertion.
4.  You’ll now be given the chance to change the control’s C++ class name and source filenames. Modify them to suit your needs or accept the default values, and then click OK.

When you’ve completed the last step, Developer Studio will query the control for its type information and will generate for you the C++ wrapper(s) you need to use the control from your own source files. You will also see a new control button on the resource editor’s control palette. Use this button to insert the control into your dialog box as you would any other control.

If you’ve completed all these steps successfully, you should have two new source files inserted into your project: webbrowser2.cpp and webbrowser2.h (it is webbrowser2.h you saw in Listing 23.1). At this point, you interact with MSIE in much the same manner you would if you were using CHtmlView. It really is that easy.

You can see this in action if you examine the WebDlg sample program I included with this chapter (shown in Figure 23.3). It has the same basic functionality as WebApp. The major difference is that WebDlg is a dialog-based MFC application that deals with MSIE through the Developer Studio wrappers rather than CHtmlView.

Now that you know how to load MSIE and use it as a browser, it’s time to see how to deal with the DHTML aspects of MSIE. Fair warning, though! This involves a heavy dose of COM. I recommend you review the first half of Chapter 16, “Using MFC and ATL,” because understanding ATL will simplify your code tremendously in this section.


Figure 23.3  The WebDlg sample program.

Using DHTML

It’s important to realize that dynamic HTML is an object-oriented view of plain old HTML. Microsoft took HTML and built an object model around the HTML tagging structure. The result of this is that you now have a COM object for each and every HTML tag, and you are free to do many things with the HTML source document given a corresponding DHTML COM object. That COM object is what makes the HTML document dynamic. You can query the DHTML COM object for the text contained within a given set of tags. Or, you can ask the DHTML COM object for the HTML source contained within the tags. You’re free to modify both the text and the HTML source.

The goal of DHTML is to reduce the dependency upon the server for document modifications. Of course, significant content changes generally require a server connection. But minor changes, and especially changes to a given HTML document’s style or appearance, may not require server interaction. That’s where DHTML steps in.

Browsers that implement the DHTML object model allow embedded scripts to manipulate the HTML document contents. You might consider the HTML document to be self-modifying. Even though the intent of DHTML is to allow the document to access its properties and make runtime modifications, it is possible to access the HTML document from Visual C++ (or Visual Basic or J++).

DHTML was designed for scripted access, so it’s a natural fit for Visual Basic and J++. Those languages were designed with the scripting COM interfaces in mind. It is a bit more difficult to use DHTML from Visual C++, if only because you must access the DHTML COM objects directly instead of using the scripting features of each interface. As an example, let’s say you want to change the title of the document after it has been downloaded. From Visual Basic, you would execute code similar to this:

window.document.title = “New Title”



However, the same operation in Visual C++ would require the code you see in Listing 23.3.

Listing 23.3 Changing the Title of an HTML Document


CComPtr<IDispatch> pDocDispatch = m_CMSIE.GetDocument();

// Check to see that you do have a document. The
// HTML document pointer will be NULL if the
// browser was unable to load the HTML file.
if ( pDocDispatch.p != NULL ) {
   // You have a document, so now query for the
   // IHTMLDocument2 interface
   CComQIPtr<IHTMLDocument2,&IID_IHTMLDocument2>
   ÄpHTMLDocument2(pDocDispatch);
   if ( pHTMLDocument2.p != NULL ) {
      // You have an active document, so retrieve the
      // element collection.
      CComPtr<IHTMLElementCollection> pElements;
      HRESULT hr = pHTMLDocument2->get_all(&pElements);
      if ( SUCCEEDED(hr) ) {
         // Pull the elements and insert into the string
         // list.
         long iNumElements;
         hr = pElements->get_length(&am;piNumElements);
         if ( SUCCEEDED(hr) && iNumElements ) {

            // Loop through the number of items
            // and pull its IHTMLElement interface
            // pointer.
           for ( long i = 0; i < iNumElements; i++ ) {
              // Retrieve item in collection
              CComVariant varName(i);
              varName.ChangeType(VT_UINT);
              CComVariant varIndex;
              CComPtr<IDispatch> pDisp;
              hr = pElements->item(varName,varIndex,&pDisp);
              if ( SUCCEEDED(hr) ) {
                // You have the element, so retrieve its
                // tag name.
                CComQIPtr<IHTMLElement, &IID_IHTMLElement>
                ÄpElement(pDisp);
                if ( pElement.p != NULL ) {
                   // Pull the tag string
                   CString strTag(_T(“???”));
                   CComBSTR bstrTag;
                   hr = pElement-t;get_tagName(&bstrTag);
                  if ( bstrTag ) {
                     strTag = bstrTag.m_str;
                  } // if

                  // Determine the tag and pull
                  // additional info depending...
                  if ( !strTag.CompareNoCase(“TITLE”) ) {
                     // Title
                     CComBSTR bstrNewTitle(_T(“New Title”));
                     CComQIPtr<IHTMLTitleElement,
                     Ä&IID_IHTMLTitleElement> pTitle(pDisp);
                     if ( pTitle.p ) {
                        hr = pTitle->put_text(bstrText);
                        if ( FAILED(hr) ) {
                           // Didn’t work...
                           AfxMessageBox(“Unable to set the
                           Ädocument title”,
                                           MB_OK | MB_ICONERROR);
                        } // if
                     } // if
                  } // if
               } // if
            } // if
         } // for
      } // if
   } // if
} // if
else {
   // You couldn’t retrieve the IHTMLDocument2
   // interface.
   AfxMessageBox(“Unable to retrieve the active document”,
                     MB_OK | MB_ICONERROR);
   } // else
} // else

Don’t worry about understanding Listing 23.3 right now. That’s what I’ll concentrate on for the remainder of the chapter. Conceptually, though, the methodology used in the Basic code and the Visual C++ code is the same. The effect of the operation is identical—the document’s title is changed. The difference is that you deal with COM in a different fashion when you program COM in C++ versus Visual Basic or J++. You have much more control over the operation in Visual C++, but the cost of this additional control is added COM support code. I’ll revisit this code when I discuss the details of using the DHTML interfaces in an upcoming section. It’s now time to examine the DHTML object model at a higher level than the COM interface.

The DHTML Object Model

DHTML starts with the document object, which represents the HTML text stored within MSIE. Perhaps a brief example will be informative. Here is a very simple HTML document:

<html>

<head>
<title>Simple HTML Document</title>
</head>

<body>

<p><strong>Hello, World!</strong></p>
</body>
</html>

The <html>...</html> tag pair denotes the document. Everything contained within these tags is considered part of the document. Most current browsers (generally) ignore anything outside the tag pair. The document has a header, as defined by the <head>...</head> tag pair. Within the header, there is a document title as indicated by the <title>...</title> tag pair. The document has a body (<body>...</body>), which is where you’ll find the specific content of the document. This particular document contains a single paragraph, defined by the paragraph tags <p>...</p>, and the single line of text within the paragraph is to be rendered in bold font, which is indicated by the <strong>...</strong> tag pair. Figure 23.4 provides a visual description of this structure.


Figure 23.4  Graphical depiction of the simple HTML document.



There are two ways to look at Figure 23.4. First, there is a hierarchical view. This view has the document as the top-level object, with the header and body as subobjects, and so on. The second way to look at Figure 23.4 is one of containment. The document contains a header and a body, and each of them contains collections of other elements (objects). As it happens, DHTML borrows from both of these views. Table 23.2 provides you with a complete listing of the current (Internet Explorer 4.01) DHTML objects.

Table 23.2 DHTML Objects

Object Interface Collection

Style IHTMLStyle None
RuleStyle IHTMLRuleStyle None
FiltersCollection IHTMLFiltersCollection All
Element IHTMLElement All
Databinding IHTMLDatabinding None
StyleSheetRule IHTMLStyleSheetRule None
StyleSheetRulesCollection IHTMLStyleSheetRulesCollection All
StyleSheetsCollection IHTMLStyleSheetsCollection All
StyleSheet IHTMLStyleSheet Stylesheets
LinkElement IHTMLLinkElement Links
TxtRange IHTMLTxtRange None
TextRangeMetrics IHTMLTextRangeMetrics None
FormElement IHTMLFormElement Forms
ControlElement IHTMLControlElement All
TextElement IHTMLTextElement None
TextContainer IHTMLTextContainer None
ControlRange IHTMLControlRange None
ImgElement IHTMLImgElement Images
InputImage IHTMLInputImage None
ImageElementFactory IHTMLImageElementFactory None
BodyElement IHTMLBodyElement All
FontElement IHTMLFontElement All, Fonts
AnchorElement IHTMLAnchorElement All, Anchors
LabelElement IHTMLLabelElement All
ListElement IHTMLListElement All
UListElement IHTMLUListElement All
OListElement IHTMLOListElement All
LIElement IHTMLLIElement All
BlockElement IHTMLBlockElement All
DivElement IHTMLDivElement All
DDElement IHTMLDDElement All
DTElement IHTMLDTElement All
BRElement IHTMLBRElement All
DListElement IHTMLDListElement All
HRElement IHTMLHRElement All
ParaElement IHTMLParaElement All
ElementCollection IHTMLElementCollection All
HeaderElement IHTMLHeaderElement All
SelectElement IHTMLSelectElement All
SelectionObject IHTMLSelectionObject None
OptionElement IHTMLOptionElement All
OptionElementFactory IHTMLOptionElementFactory None
InputHiddenElement IHTMLInputHiddenElement All
InputTextElement IHTMLInputTextElement All
TextAreaElement IHTMLTextAreaElement All
InputButtonElement IHTMLInputButtonElement All
ButtonElement IHTMLButtonElement All
InputFileElement IHTMLInputFileElement All
MarqueeElement IHTMLMarqueeElement All
OptionButtonElement IHTMLOptionButtonElement All
TitleElement IHTMLTitleElement All
MetaElement IHTMLMetaElement All
BaseElement IHTMLBaseElement All
IsIndexElement IHTMLIsIndexElement All
NextIdElement IHTMLNextIdElement All
BaseFontElement IHTMLBaseFontElement All
UnknownElement IHTMLUnknownElement All
MimeTypesCollection IHTMLMimeTypesCollection All
PluginsCollection IHTMLPluginsCollection All
OpsProfile IHTMLOpsProfile None
Location IHTMLLocation None
EventObj IHTMLEventObj None
FramesCollection2 IHTMLFramesCollection2 All
Screen IHTMLScreen None
Window2 IHTMLWindow2 None
DocumentEvents HTMLDocumentEvents None
Document IHTMLDocument None
Document2 IHTMLDocument2 None
EmbedElement IHTMLEmbedElement All
AreasCollection IHTMLAreasCollection All
MapElement IHTMLMapElement All
AreaElement IHTMLAreaElement All, Links
TableCaption IHTMLTableCaption None
CommentElement IHTMLCommentElement All
PhraseElement IHTMLPhraseElement All
SpanElement IHTMLSpanElement All
Table IHTMLTable None
TableCol IHTMLTableCol None
TableSection IHTMLTableSection None
TableRow IHTMLTableRow None
TableRowMetrics IHTMLTableRowMetrics None
TableCell IHTMLTableCell None
ScriptElement IHTMLScriptElement All, Scripts
NoShowElement IHTMLNoShowElement All
ObjectElement IHTMLObjectElement All, Objects
FrameBase IHTMLFrameBase None
FrameElement IHTMLFrameElement All, Frames
IFrameElement IHTMLIFrameElement All, Frames
DivPosition IHTMLDivPosition None
LegendElement IHTMLLegendElement All
FrameSetElement IHTMLFrameSetElement All
BGsound IHTMLBGsound None
StyleElement IHTMLStyleElement All, Styles
StyleFontFace IHTMLStyleFontFace None



Clearly, from the sheer size of Table 23.2, you can see that there is a lot to DHTML, and it won’t be possible to cover every aspect of each DHTML object. I will introduce the major objects, however, and several sample programs are included with this chapter to show you specifically how to use many of the objects you commonly encounter. I’ll start with the granddaddy of all DHTML objects, the document object.

The Document Object

The DHTML document object is the most important and powerful DHTML object. After all, without a document, you have no Web page. You request access to the document object from the browser, and likewise you request access to other DHTML objects from the document object. Figure 23.5 shows you the document object and the element collections it contains as well as the corresponding HTML tags.

What Figure 23.5 says is: If you want to access all the anchors within the document, as the DocNavigator sample program included with this chapter shows you, you first get the document object, and then get the anchors collection from the document object.

Note the first collection, All. This collection is, in many cases, the only collection available to you for such things as the header and body objects, lists, tables, and many other HTML tagged structures. I’ll refer back to this object when I discuss other important DHTML objects in the next section.

Aside from obtaining these collections, the document also provides mechanisms for determining and changing many aspects of the document. For example, you can easily change the foreground and background color. Or, you could change the colors of the links. You can even check on the security aspects of the document. The specific methods and properties are far too numerous to list here. I will show you how the document object is used and what methods are most commonly called later in the chapter when I discuss the COM aspects of the DHTML objects. The Visual C++ online documentation provides the other details. For now, let’s examine some of the other DHTML objects you will commonly encounter.


Figure 23.5  The DHTML document object and its collections.

Other DHTML Objects

Oddly, there isn’t a single DHTML object for the header, though there is one for the document body. The header information is instead broken down into the separate header parts, such as the title, metadata, and style information. Each of these has its own DHTML object.

The document body is further broken down into many objects, essentially an object for each HTML tag, to include objects for links, anchors, lists, and images, to name a few. Some of the body objects may be found in DHTML collection objects, whereas others simply exist in the document singly or in quantity. Table 23.2 lists the DHTML objects you will encounter and whether you will find them in a collection object or not.

Collection Objects

Some HTML tags are particularly interesting (at least according to the designers of DHTML), such as links and images, or stylesheets. These tag sets (the beginning and ending tags shown in Figure 23.5) are anointed with special object status. They are collected when the document is loaded and presented to you when you request their COM interfaces directly from the document object. There are other collections, however, and the complete list is shown in Table 23.2. There are some notable tags that do not have collections, such as paragraphs (<p>...</p>) and tables (<table>...</table>). If you are interested in these HTML document objects, you will have to access the All collection object to find them.

For each tag set, you have access to all the tag pairs within the document directly from the related collection object. For example, if you want to access all the document’s images (by URL), you request the image collection from the document and iterate through the collection to access the individual image DHTML objects.

DHTML COM Interfaces

As you saw from Table 23.2, there are many DHTML objects and associated COM interfaces. Which interface you use and how you obtain it depends upon the interface. Several interfaces are actually polymorphic, in that the given interface could be used in several ways depending upon how you obtained the interface pointer. Luckily, the majority of the DHTML interfaces follow a pattern, which I’ll discuss next.


Note:  

In order to compile C++ code accessing DHTML interface, you must include the MSHTML.H header file. This file declares all the DHTML interfaces and events.


Obtaining the Document DHTML Interface

You begin accessing the DHTML aspects of the document by requesting a document pointer from the browser using one of three methods depending upon how you loaded MSIE. If you’ve loaded MSIE as an ActiveX control (m_CMSIE in the following example), this code retrieves the document COM object’s IDispatch interface using ATL and the webbrowser2.h code you saw in Listing 23.1:

// m_CMSIE is a CWnd object encapsulating the MSIE ActiveX control
CComPtr<IDispatch> pDocDispatch = m_CMSIE.GetDocument();

If you’re using CHtmlView, you retrieve the document using the CHtmlView ::GetHtmlDocument() method:

// The “this” pointer is CHtmlView*
CComPtr<IDispatch> pDocDispatch = GetHtmlDocument();

If you have a pointer to the browser COM object (IWebBrowser2), your code would look like this:

// pMSIE is a (valid) IWebBrowser2 COM interface pointer
CComPtr<IDispatch> pDocDispatch;
HRESULT hr = pMSIE->get_document(&pDocDispatch);

When you have the IDispatch-based pointer, you need to cast it (in COM terms) to a IHTMLDocument2 interface pointer, which you do using IUnknown::QueryInterface():

if ( pDocDispatch.p != NULL ) {
   // You have a document, so now query for the
   // IHTMLDocument2 interface
   CComQIPtr<IHTMLDocument2,&IID_IHTMLDocument2>
   ÄpHTMLDocument2(pDocDispatch);
   if ( pHTMLDocument2.p != NULL ) {
      // You have an active document
   } // if
} // if
else {
   // Handle the error condition...
} // else

When you have the IHTMLDocument2 interface pointer, you’re free to begin your DHTML work. Note that however you obtain the document object interface pointer, the pattern is the same. You first run the browser, navigate to a URL (local or remote), and finally request a document interface pointer from the browser (which you convert from IDispatch to IHTMLDocument2).


CAUTION:  

If you have MSIE loaded but have not navigated to a URL (and loaded the resulting HTML document), you will get an error when you request the document interface pointer. There is no document, so no valid document interface pointer is available.


When you have a valid document interface pointer, you can call any of the IHTMLDocument2 methods to begin accessing the other DHTML objects.



Obtaining the DHTML Collections Interfaces

The IHTMLDocument2 interface has several methods for obtaining collection object pointers, the foremost of which is the All collection (IHTMLElementCollection). This code, for example, obtains the All collection from the document:

// Retrieve the “all” element collection.
CComPtr<IHTMLElementCollection> pElements;
HRESULT hr = pHTMLDocument2->get_all(&pElements);
if ( SUCCEEDED(hr) ) {
   // Deal with the collection elements through pElements...
} // if

IHTMLElementCollection is a generic HTML collection object, which in this case (the All collection) is filled with all the DHTML objects the document contains.

You need not deal with all the document tags, however. As I mentioned previously, the document object exports several collection interface pointers. If, for example, you were interested in access to all the hyperlinks the document contained (for validation purposes, perhaps), you could directly access that collection using the IHTMLDocument2::get_links() method. This method returns to you an IHTMLElementCollection interface pointer you use for iteration. You’ll see how this is done in the next section. Table 23.3 provides you with the IHTMLDocument2 methods used to retrieve the major element collections.

Table 23.3 IHTMLDocument2 Collection Export Methods

Collection IHTMLDocument2 Method

All get_all()
Anchors get_anchors()
Applets get_applets()
Embeds get_embeds()
Forms get_forms()
Frames get_frames()
Images get_images()
Links get_links()
Plug-Ins get_plugins()
Scripts get_scripts()
Style sheets get_styleSheets()

You don’t see the embeds and plug-ins collections in Figure 23.5 because they are not standard HTML objects. Rather, they have to do with embedded objects (a lˆ OLE), so I omitted them from Figure 23.5. However, they are collections like any other, and if you are interested in them, you can access their collections as you would any other.

When you’ve obtained a DHTML collection object, it’s time to retrieve the individual DHTML objects from the collection and actually do something with them. As it happens, that is a topic for the next section.

Using the DHTML Interface

Obtaining the collections objects isn’t too difficult, if you obtained the document object in the first place (and assuming there are any elements within the document to merit the creation of the collection). If there are any elements contained in the collection, you’ll need to iterate through the collection and withdraw each element. This is a bit more involved.

The basic recipe for iterating through a collection is this:

1.  Loop through the elements in the collection.
2.  Query each element for its tag name.
3.  Compare the tag name to the tags you want to intercept.
4.  If the tag names compare favorably, QueryInterface() the element interface pointer for the specific tag interface pointer.
5.  Access the specific tag type’s information.
6.  Release all the interfaces.

Of course, you may skip steps 2 and 3 if you already know the collection contains a specific tag type, possibly because you requested a certain collection from the document. In any case, assuming you have a valid IHTMLElementCollection pointer, you can begin iterating through the collection using this code (step 1 in the preceding list):

// The pElements pointer is IHTMLElementCollection
long iNumElements;
HRESULT hr = pElements->get_length(&iNumElements);
   if ( SUCCEEDED(hr) && iNumElements ) {

      // Loop through the number of items
      // and pull its IHTMLElement interface
      // pointer.
      for ( long i = 0; i < iNumElements; i++ ) {
         // Retrieve item in collection
         CComVariant varName(i);
         varName.ChangeType(VT_UINT);
         CComVariant varIndex;
         CComPtr<IDispatch> pDisp;
         hr = pElements->item(varName,varIndex,&pDisp);
         if ( SUCCEEDED(hr) ) {
            // You have the element, so retrieve its
            // tag name.
            CComQIPtr<IHTMLElement, &;IID_IHTMLElement>
            ÄpElement(pDisp);
            if ( pElement.p != NULL ) {
               // You now have an individual element from
               // the collection...
            } // if
         } // if
   } // for
} // if



For each iteration, you will be retrieving an IHTMLElement object from the collection. Like IHTMLElementCollection, IHTMLElement is also a generic DHTML interface. To get any really useful information from it, you must first determine what the element contains, if you don’t already know, and then query the interface for the specific DHTML interface designed to deal with that tag. The following code snippet pulls the tag’s text, which is contained in the IHTMLElement object. When you know the tag’s text, you can easily compare that text to specific tags you want to handle and query the IHTMLElement interface accordingly. As an example, in this case I am looking specifically for the document title so that I can modify it. This code implements steps 2 through 6 in the preceding list.

if ( pElement.p != NULL ) {
   // Pull the tag string
   CString strTag(_T(“???”));
   CComBSTR bstrTag;
   pElement->get_tagName(&bstrTag);
   if ( bstrTag ) {
      strTag = bstrTag.m_str;
   } // if

   // Determine the tag and pull
   // additional info depending...
   if ( !strTag.CompareNoCase(“TITLE”) ) {
      // Title
      CComBSTR bstrNewTitle(_T(“New Title”));
      CComQIPtr<IHTMLTitleElement,
      Ä&IID_IHTMLTitleElement> pTitle(pDisp);
      if ( pTitle.p != NULL ) {
         hr = pTitle->put_text(bstrText);
         if ( FAILED(hr) ) {
            // Didn’t work...
            AfxMessageBox(“Unable to set the document title”,
                              MB_OK | MB_ICONERROR);
         } // if
      } // if
   } // if
} // if

The methods any given DHTML object exposes depend upon the object itself, but there are several common methods you could use. In the preceding example, I replaced the text between the <title>...</title> tags using the IHTMLTitleElement::put_Text() method. I could have used a method from the element object, IHTMLElement::put_innerText(). The outcome will be the same—the text between the tags will be replaced with the text I provide. The element object is a key object when dealing with individual HTML tag sets because all HTML tags found in the document are given an element object. Because of this emphasis, I’ll introduce some of the DHTML element object methods and describe their use in the next section.

The DHTML Element Interface Methods

You could think of the DHTML element object as the object common to all HTML tags within the document. A specialized DHTML object, such as the IHTMLTitleElement object you just saw in action, actually inherits behavior from IHTMLElement. All the DHTML objects found in a collection exhibit this inheritance relationship, and because all HTML tags wind up in some collection (if only the All collection), they all share these common methods. Specialization objects like IHTMLTitleElement simply add the methods related to the associated HTML tag, which in this case is the <title>...</title> tag pair. Table 23.4 shows you the more commonly used IHTMLElement methods.

Table 23.4 Commonly Used IHTMLElement Methods

Method Purpose

get_tagName() Retrieve the HTML tag text
get_parentElement() Retrieve parent element pointer
get_document() Retrieve the document’s IDispatch pointer
get_innerHTML() Retrieve HTML between object’s tags
get_innerText() Retrieve text between object’s tags
get_outerHTML() Retrieve HTML including object’s tags
put_innerHTML() Replace the enclosed HTML
get_innerText() Replace the enclosed text
get_outerHTML() Replace the object and enclosed HTML
get_children() Retrieves the collection containing the object’s immediate children
get_all() Retrieves the collection containing the objects in the HTML document within the scope of this object
toString() Returns a string representation of the object

Many of the IHTMLElement methods are self-explanatory, but the inner and outer functions may not be so obvious. Inner functions, such as IHTMLElement::get_innerText(), retrieve the text (or HTML) between the tags the object supports. As an example, assume you have this HTML within your document:

<body>
<p>
   This is paragraph 1.
</p>
<p>
   <font face=“Arial”>
      This is paragraph 2, with a different font.
   </font>
</p>
</body>

If you have an IHTMLElement object that represents the second paragraph, it contains this HTML:

<p>
   <font face=“Arial”>
      This is paragraph 2, with a different font.
   </font>
</p>

If you call the IHTMLElement::get_innerText() method, you will retrieve the text “This is paragraph 2, with a different font.”, because this is the textual representation of what exists between the second <p>...</p> tag set. If you called IHTMLElement::put_innerText(), you would replace the text range with the text you provided. This code, for example, replaces the text with the string “This was replaced!”:

CComBSTR bstrText(_T(“This was replaced!”));
pPara2Element ->put_innerText(bstrText);

If you would rather deal with HTML instead of text, because the text methods filter the HTML tags from the text stream, you would use the inner and outer HTML methods. Using this same example, retrieving the inner text from the second paragraph DHTML object

<p>
   <font face=“Arial”>
      This is paragraph 2, with a different font.
   </font>
</p>

and the font object

<font face=“Arial”>
      This is paragraph 2, with a different font.
</font>

would result in the same string, This is paragraph 2, with a different font.. But if you requested the inner HTML of the paragraph object, you would receive the string <font face=“Arial”>This is paragraph 2, with a different font.</font>. Note that the HTML tags have not been filtered.



Using the IHTMLElement::put_innerHTML() method, you could replace the entire HTML stream within the given DHTML object. Using the same replacement example, you could change the HTML the second paragraph object contained to read “This was replaced!” in bold font:

<p>
   <strong>
      This was replaced!
   </string>
</p>

This code would do the trick:

CComBSTR bstrHTML(_T(“<strong> This was replaced! </string>”));
pPara2Element ->put_innerHTML(bstrHTML);

The outer methods are similar in that they retrieve and replace text from the given object’s tag set. The main difference is, however, that the entire object is referenced or modified. Using the IHTMLElement::put_outerHTML() method of the second paragraph’s DHTML object, you could change the object from a paragraph object to an unordered list object in a single function call. The HTML

<p>
   <font face=“Arial”>
      This is paragraph 2, with a different font.
   </font>
</p>

would change to the HTML

<ul>
   <li>
      This is a list item.
   </li>
</ul>

using this code:

CComBSTR bstrHTML(_T(“<ul><li> This is a list item.</li></ul>”));
pPara2Element->put_outerHTML(bstrHTML);

As you can see, the outer methods are quite powerful! You not only change the document, but you also possibly rearrange the collections of DHTML objects.

Document Navigation

As you saw earlier in Table 23.2, there are a tremendous number of DHTML objects and interfaces. Just describing them would fill an entire book and is beyond the scope of this chapter. However, I did want to share a real-world DHTML and Visual C++ application, that is, a C++ application that is capable of navigating through an HTML document.

The specifications of the application were such that HTML documents (search result reports) would be created on the server and transmitted to a client computer for viewing. The application’s product manager envisioned a tree control in a left-hand pane of a split view with the HTML document visible in the right-hand pane. The idea was that the tree control would contain the HTML bookmarks (anchors) found in the document. As the reports could be quite large, the application’s user would simply find the bookmark of interest to them in the tree view, select it, and have the HTML view automatically scroll to that location. DHTML seemed a natural fit to fill this requirement.

The DocNavigator sample program included with this chapter is a short sample application that does the same thing. When you open a file (or navigate to a URL), the anchor collection is extracted and its contents are displayed in a tree control. When you click a node in the tree control, the document automatically scrolls to the bookmark. Figure 23.6 shows DocNavigator in action.

The DHTML aspects of this application are very similar to those you have seen in this chapter. I load the document, obtain the document pointer, and retrieve the anchors collection using IHTMLDocument2::get_anchors(). When I actually access the anchor elements, I use the code found in Listing 23.4.


Figure 23.6  The DocNavigator sample program.

Listing 23.4 DHTML Document Anchor Retrieval


CComQIPtr<IHTMLElement,
   &IID_IHTMLElement> pElement(pDisp);
if ( pElement != NULL ) {
   CComBSTR bstrAnchor;
   CComQIPtr<IHTMLAnchorElement,
      &IID_IHTMLAnchorElement> pAnchor(pDisp);
   if ( pAnchor ) {
      // Was a link...
      pAnchor->get_name(&bstrAnchor);
      if ( !bstrAnchor ) {
         // Not a bookmark...
         continue;
      } // if

      // Retrieve anchor text
      CString strAnchor;
      strAnchor = bstrAnchor.m_str;
      CString* pstrBookmark = new CString(strAnchor);

      // Retrieve anchor text name
      pElement->get_innerText(&bstrAnchor);
      if ( !bstrAnchor ) {
         // No text...
         strAnchor = _T(“???”);
      } // if
      else {
         strAnchor = bstrAnchor.m_str;
      } // else

      // Add this anchor to the tree
      GetTreeCtrl().InsertItem(TVIF_TEXT | TVIF_PARAM,
         strAnchor,
         0,0,0,0,
         (LPARAM)pstrBookmark,
         reinterpret_cast<HTREEITEM>(TVI_ROOT),
         reinterpret_cast<HTREEITEM>(TVI_LAST));

      // Continue to next anchor
      continue;
   } // if
} // if

The interesting thing to note about the code in Listing 23.4 is that the IDispatch interface you receive from the IHTMLElementCollection is both an IHTMLElement interface and an IHTMLAnchorElement interface. How you use the interface depends upon what information you want to retrieve from the interface. In this case, you can use the IHTMLElement interface to retrieve the text associated with the anchor. After you have the IHTMLElement interface, then you can query for and use the IHTMLAnchorElement interface to retrieve the actual anchor text. The anchor’s text is what the browser will require when it is instructed to navigate to the bookmark when a tree item is selected. You can see this in Listing 23.5.

Listing 23.5 Tree View Anchor Selection


void CSplitTreeView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
{
   NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

   // Examine notification structure to determine new
   // selection
   if ((pNMTreeView->itemNew.state & TVIS_SELECTED) &&
         (pNMTreeView->action)) {
      CString* pstrBookmark = reinterpret_cast<CString*>
      Ä(pNMTreeView->itemNew.lParam);
      ASSERT(!pstrBookmark->IsEmpty());
      if ( !pstrBookmark->IsEmpty() ) {
         CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
         pFrame->Navigate((LPCTSTR)pstrBookmark->GetBuffer(0));
      } // if
   } // if

   *pResult = 0;
}



Because I stored a CString containing the anchor text as the lParam of the tree node, I simply retrieve the node’s lParam data and pass the text I find there to the frame window, which then passes the text to the browser view.

The browser’s job is to navigate to the bookmark. This isn’t as obvious as using the CHtmlView::Navigate2() method (or navigating directly using MSIE). If you truly navigate, in browser terms, you are attempting to load a new document. So CHtmlView::Navigate2() simply won’t do. You don’t want to load or reload a document—you really want to have the browser scroll to the given anchor.

The solution to this is, again, DHTML. The IHTMLLocation object has a method put_hash() designed just for anchor scrolling. A hash is the portion of the URL that specifies a bookmark, as in this URL:

http://www.somesite.com/index.htm#introduction

The hash portion of this imaginary URL is italicized. The text following the # is the actual bookmark (anchor) the browser will use for navigation. In this case, I take advantage of this capability using DHTML. Listing 23.6 shows you how I do this.

Listing 23.6 DHTML Browser Anchor Scrolling


void CDocNavigatorView::NavigateAnchor(LPCTSTR strItem)
{
   // Navigate to an internal bookmark
   if ( strItem != NULL ) {
      // Retrieve the document’s IDispatch pointer
      CComPtr<IDispatch> pDocDispatch = GetHtmlDocument();

      // QI for the IHTMLDocument2 pointer
      CComQIPtr<IHTMLDocument2,&IID_IHTMLDocument2>
      ÄpHTMLDocument2(pDocDispatch);
      if ( !pHTMLDocument2 ) {
         AfxMessageBox(_T(“IHTMLDocument2 interface not supported.”));
         // This version of Internet Explorer doesn’t
         // support DHTML...
         ASSERT(FALSE);
         return;
      } // if

      // Retrieve the IHTMLLocation pointer
      CComPtr<IHTMLLocation> pHTMLLocation;
      HRESULT hr = pHTMLDocument2->get_location(&pHTMLLocation);
      if ( FAILED(hr) ) {
         AfxMessageBox(_T(“Error retrieving IHTMLLocation
         Äinterface.”));
         // Unknown internal Internet Explorer error...
         ASSERT(FALSE);
         return;
      } // if

      // Navigate to the bookmark (known as a “hash”,
      // which is the address portion of the URL
      // following the pound, ‘#’)
      CComBSTR bstrHash(strItem);
      pHTMLLocation->put_hash(bstrHash);
      // Note no check of the return code...it either
      // navigated or it didn’t...if you wanted to check
      // the HRESULT for some error condition, feel free
      // to do so.
   } // if
}

The IHTMLLocation::put_hash() method does all the magic. Most of the remaining code in Listing 23.6 simply sets up the interface (as you’ve seen previously in this chapter).

Summary

Now you’ve seen the basics of using DHTML from Visual C++. It isn’t as easy as using DHTML from Visual Basic or J++, but then, you have much more control, too. I’ve merely scratched the surface with respect to what the DHTML objects can do for you, so be sure to examine the online documentation for specific interface method help. DHTML is a rich and powerful object model and well worth your time to know and use if you routinely work with HTML-based documents and Visual C++!